#!/usr/bin/env python3
#操作SDCC编译器的类，3个传参：工程名(绝对路径),文件列表,QtextBroaser

import os,  shutil
import subprocess
from PyQt5.QtWidgets import QApplication,  QMessageBox
class compileProject:
    def __init__(self, win):
        if win == None: 
            return
        self.win = win  #主界面作为参数传入，可以直接调用主界面的参数
        #1. 编译器路径选择
        if self.win.isComplierPathDefault == '1':
            self.compliePath = self.win.Code51_dir+'/sdcc'
        else:
            self.compliePath = self.win.compilePath
        #2. 编译参数选择
        if self.win.isXdataDefault == '1':
            self.XdataLen = '8192'
        else:
            self.XdataLen=self.win.XdataLen
        #3. 工程文件列表
        self.fileList = win.file_list
        #4. 信息输出视图
        self.browser=win.text_browser
        self.browser.setText('')
        #5. 工程名称
        self.cbpDir, cbpName = os.path.split(win.fullfile)
        self.outputName, extension = os.path.splitext(cbpName)
        self.objFolder =self.cbpDir +'/obj'
        
    def get_hex_info(self, filename):    
        try:
            with open(filename, 'r') as fin:
                input_file = fin.read()
                input_str = input_file.splitlines(False)  
                max_line = len(input_str) 
                if max_line >=29:
                    strlist=input_str[20].split() #   取strlist[3] -- data count
                    data_count=str(int(strlist[3], 16)-1)
                    strlist=input_str[27].split() #   取strlist[4]
                    xdata_count=str(int(strlist[4], 10))
                    strlist=input_str[28].split() #   取strlist[3]
                    code_count=str(int(strlist[3], 10))
                    return data_count, xdata_count, code_count
                pass
        except:
            return None
    def projectClean(self): # 删除工程目录下子文件夹《obj》里面的全部文件
        if os.path.exists(self.objFolder):       
            try:
                shutil.rmtree(self.objFolder)   #强制移除空文件夹
                os.mkdir(self.objFolder)        #新建空文件夹
            except OSError:
                self.browser.append("<font color=hotpink>%s </font> " %   
                                            "文件夹 {0} 被占用。".format(self.objFolder))

    def CreatObjFolder(self):   # 传入工程目录,在工程目录下新建obj文件夹
        if not os.path.exists(self.objFolder):            
            os.mkdir(self.objFolder)    #创建 空文件夹
        
    def complie_fileBySDCC(self, abs_file_name):    # 编译C文件
        status = True
        
        #文件列表中，c、h文件并存，跳过h文件
        if '.h' in abs_file_name or '.H' in abs_file_name:
            return status
        (_, all_fileName)=os.path.split(abs_file_name)
        (filename,extension) = os.path.splitext(all_fileName)
        #1. 生成编译命令列表
        sdcc_path=self.compliePath+'/bin/sdcc'
        mcu_model='-mmcs51'
        options1 = '--model-large'
        options2 = '--opt-code-size'
        options3 = '--out-fmt-ihx'
        options4 = '--model-large'
        options5 = '-mmcs51'
        if 'nt' in os.name:     #winndows系统
            include_path='-I"'+self.compliePath+'/include"'
        else:       #非windows系统
            include_path ='-I"' +self.win.compileIncldue+'/include"'
        src_file =abs_file_name
        dest_file= 'obj/' +filename+'.rel'
        cmdlist=[sdcc_path, mcu_model , options1 ,options2 ,options3 ,options4 ,options5 ,
                    include_path , '-c', src_file ,'-o', dest_file ]
        #2. 在信息输出视图显示 即将执行的命令
        self.browser.append( "<font color=black>%s </font>" %" ".join(cmdlist))  
        QApplication.processEvents()
        
        #3. 执行命令
        p = subprocess.Popen(cmdlist, stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, 
                                         cwd=self.cbpDir, bufsize=1)
        #4. 打印命令执行结果--异常部分   （注意异常部分比正常部分高优先级，否则会陷入死循环） 
       
        for line in iter(p.stderr.readline, b''):
            if b'warning' in line:
                status = True
                self.browser.append("<font color=hotpink>%s </font> " %  line.decode("utf8","ignore")) 
            else:
                status = False
                self.browser.append("<font color=red>%s </font> " %  line.decode("utf8","ignore"))   
                p.stdout.close()
                p.wait()
                return status
            QApplication.processEvents()
        #5. 打印命令执行结果--正常部分
        for line in iter(p.stdout.readline, b''):
            status = False
            self.browser.append("<font color=red>%s </font> " %  line.decode())       
            QApplication.processEvents()       
        p.stdout.close()
        p.wait()
        return status
    def complieAllFile(self):
        for file in self.fileList:
            if self.complie_fileBySDCC(file) == False:
                return False
        return True
    def LinkAllFile(self):
        status = True
        #1. 生成链接命令列表
        sdcc_path=self.compliePath+'/bin/sdcc'
        if 'nt' in os.name:#winndows系统
            lib_path='-L"'+self.compliePath+'/lib"'
        else:       #非winndows系统
            lib_path='-L"'+self.win.compileIncldue+'/lib"'
        ihx_name='obj/'+self.outputName+'.ihx'
        mcu_model='-mmcs51'
        options1 = '--model-large'
        options2 = '--xram-size'
        options2_para = self.XdataLen  
        options3 = '--iram-size'
        options3_para = str(256)
        options4 = '--code-size'
        options4_para = str(65536)
        options5 = '--out-fmt-ihx'
        rel_str =''
        cmdlist=[sdcc_path,lib_path,  '-o',ihx_name,  mcu_model , options1 ,options2 ,options2_para,  
                    options3 ,options3_para ,options4 ,options4_para ,options5]
        #2. 链接命令中包含多个rel文件
        for file in self.fileList:  #提取c文件
            if '.c' in file or '.C' in file:
                (_, all_fileName)=os.path.split(file)
                (filename,extension) = os.path.splitext(all_fileName)
                rel_str = 'obj/'+filename+'.rel'
                cmdlist.append(rel_str)
        if len(rel_str) == 0:            
            return False
        #3. 在信息输出视图显示 即将执行的命令    
        self.browser.append("<font color=black>%s </font>" %" ".join(cmdlist))       
        QApplication.processEvents()
        #4. 执行命令
        p = subprocess.Popen(cmdlist, stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, 
                                        cwd=self.cbpDir, bufsize=1)
        #5. 输出异常部分
        for line in iter(p.stderr.readline, b''):
            self.browser.append("<font color=red>%s </font>" %  line.decode())    
            #logging.info(line.decode())
            QApplication.processEvents()
            #p.stdout.close()
            status = False
        #6. 输出正常部分 -- 实际上这里不会有信息输出，如果有，也是异常信息
        for line in iter(p.stdout.readline, b''):
            self.browser.append("<font color=black>%s </font>" %  line.decode())           
            QApplication.processEvents()
            #p.stdout.close()
            status = False
        p.stdout.close()
        p.wait()
        return status
    def ihx2hex(self):  
        ihxFile = self.cbpDir+'/obj/'+self.outputName+'.ihx '
        hexFile = self.cbpDir+'/obj/'+self.outputName+'.hex '
        if os.path.exists(ihxFile):
            packihx_path=self.compliePath+ '/bin/packihx'
            cmdlist=[packihx_path, ihxFile, '>',hexFile ]
            #_cmd = 'packihx ihxFile  >   hexFile'
            _cmd = " ".join(cmdlist)
            self.browser.append(_cmd)
            QApplication.processEvents()
            p = subprocess.Popen(_cmd, stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, 
                                        shell=True, cwd=self.cbpDir,  bufsize=1)    #此处一定要加 shell参数，否则没有hex文件
            #这个命令比较奇怪，正常的输出是在 p.stderr，所以如果输出结果包含 OK 就认为成功。
            for line in iter(p.stderr.readline, b''):
                if 'OK' in  line.decode():
                    self.browser.append("<font color=black>%s </font>" %  line.decode())
                    ihxFile=hexFile
                    break
                else:
                    self.browser.append("<font color=red>%s </font>" %  line.decode())
                    ihxFile = ''
            QApplication.processEvents()
            p.stdout.close()
            p.wait
            hexFile = self.cbpDir+'/obj/'+self.outputName+'.mem '
            if os.path.exists(hexFile):
                r=self.get_hex_info(hexFile)
                if r is not None:
                    self.browser.append('资源使用情况:  data:%s , xdata:%s , code:%s'%(r[0], r[1], r[2]))
                    QApplication.processEvents()
                    
            return ihxFile
        else:
            return None
            
'''            
    def testUpdate(self):
        for i in range(0, 5):            
            self.browser.insertPlainText('count % d\n'%i)
            QApplication.processEvents()
            logging.info('count % d\n'%i)
            time.sleep(1)
'''            
